<template>
  <div class="cascader">
    <!-- 输入框 -->
    <div class="cascader-input">
      <input
        type="text"
        v-model="keyword"
        :placeholder="selectedLabels.length ? '' : '请选择'"
        @focus="showDropdown = true"
        @input="onInput"
        class="cascader-input-field"
      />
      <div v-if="checkedValues.length && !keyword" class="cascader-tags">
        <span
          class="cascader-tag"
          v-for="val in checkedLeafValues"
          :key="val"
          title="点击叉号可移除"
        >
          {{ getPathText(val) }}
          <i class="tag-close" @click.stop="uncheckByValue(val)">×</i>
        </span>
      </div>
      <span
        v-if="selectedPath.length"
        class="clear-btn"
        @click.stop="clearSelection"
      >
        ✕
      </span>
    </div>

    <!-- 下拉菜单 -->
    <div v-if="showDropdown" class="cascader-dropdown">
      <!-- 如果有关键字，显示搜索结果 -->
      <template v-if="keyword">
        <div
          v-for="(path, idx) in filteredPaths"
          :key="idx"
          class="cascader-option"
          @click="selectPath(path)"
        >
          {{ path.map((o) => o.label).join(" / ") }}
        </div>
        <div v-if="!filteredPaths.length" class="no-data">未找到匹配结果</div>
      </template>

      <!-- 否则展示多列面板 -->
      <template v-else>
        <div
          class="cascader-panel"
          v-for="(levelOptions, level) in panels"
          :key="level"
        >
          <div
            class="cascader-option"
            v-for="option in levelOptions"
            :key="option.value"
            :class="{ active: isActive(option, level) }"
          >
            <!-- ✅ 多选框 -->
            <input
              type="checkbox"
              :checked="checkedValues.includes(option.value)"
              :indeterminate="halfCheckedValues.includes(option.value)"
              @change.stop="toggleCheck(option, $event.target.checked)"
            />

            <span @click="handleSelect(option, level)">
              {{ option.label }}
            </span>
            <!-- 显示加载状态 -->
            <span v-if="option.loading" class="loading">⏳</span>
            <span v-else-if="option.hasChildren" class="arrow">▶</span>
          </div>
        </div>
      </template>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, toRaw } from "vue";

const props = defineProps({
  options: { type: Array, required: true },
  loadChildren: { type: Function, default: null }, // 外部传入加载函数
});

const emit = defineEmits(["update:modelValue"]);

/** 1) 用内部响应式副本，禁止直接改 props */
const innerOptions = ref([]);

// 初始化 / props 变化时，同步一份深拷贝到本地
watch(
  () => props.options,
  (val) => {
    innerOptions.value = deepClone(val); // 👈 深拷贝，保证本地是可变且响应的
  },
  { immediate: true, deep: false }
);

// 状态
const showDropdown = ref(false);
const keyword = ref("");
const selectedPath = ref([]);

const checkedValues = ref([]); // 完全选中的节点
const halfCheckedValues = ref([]); // 半选的节点

// 切换选中状态
function toggleCheck(option, checked) {
  console.log("toggleCheck", option, checked);

  if (checked) {
    addChecked(option); // 勾选：选中自己和所有子孙
  } else {
    removeChecked(option); // 取消：去掉自己和所有子孙
  }
  // 自底向上修正所有祖先
  syncAncestors(option);
  // 同步 v-model（如果你想让 v-model 返回选中的 value 列表）
  emit("update:modelValue", checkedValues.value.slice());
}

// ✅ 勾选：递归选中所有子节点
function addChecked(option) {
  const values = getAllChildrenValues(option);
  values.push(option.value);
  checkedValues.value = Array.from(
    new Set([...checkedValues.value, ...values])
  );
  // 子孙不可能是半选，清理掉
  halfCheckedValues.value = halfCheckedValues.value.filter(
    (v) => !values.includes(v)
  );
}

// ✅ 取消勾选：递归取消当前节点及子节点
// 取消：递归移除自己与所有子孙
function removeChecked(option) {
  const values = getAllChildrenValues(option);
  values.push(option.value);
  checkedValues.value = checkedValues.value.filter((v) => !values.includes(v));
  halfCheckedValues.value = halfCheckedValues.value.filter(
    (v) => !values.includes(v)
  );
}

// 递归获取所有子节点的 value
function getAllChildrenValues(option) {
  let vals = [];
  if (option.children?.length) {
    for (const c of option.children) {
      vals.push(c.value);
      vals = vals.concat(getAllChildrenValues(c));
    }
  }
  return vals;
}

// === 自底向上修正所有祖先节点的三态 ===
function syncAncestors(node) {
  let parent = findParent(innerOptions.value, node.value);
  while (parent) {
    applyParentState(parent);
    parent = findParent(innerOptions.value, parent.value);
  }
}

// 根据直接子节点状态，修正 parent 的三态
function applyParentState(parent) {
  const children = parent.children || [];
  if (children.length === 0) return;

  // 子节点是否“全选”
  const isChildChecked = (v) => checkedValues.value.includes(v);
  const isChildHalf = (v) => halfCheckedValues.value.includes(v);

  const allChildChecked = children.every((c) => isChildChecked(c.value));
  const noneChildSelected = children.every(
    (c) => !isChildChecked(c.value) && !isChildHalf(c.value)
  );

  if (allChildChecked) {
    // 父：全选
    if (!checkedValues.value.includes(parent.value)) {
      checkedValues.value.push(parent.value);
    }
    // 父不能半选
    halfCheckedValues.value = halfCheckedValues.value.filter(
      (v) => v !== parent.value
    );
  } else if (noneChildSelected) {
    // 父：未选
    checkedValues.value = checkedValues.value.filter((v) => v !== parent.value);
    halfCheckedValues.value = halfCheckedValues.value.filter(
      (v) => v !== parent.value
    );
  } else {
    // 父：半选（只要有一个子是半选或部分选中）
    checkedValues.value = checkedValues.value.filter((v) => v !== parent.value);
    if (!halfCheckedValues.value.includes(parent.value)) {
      halfCheckedValues.value.push(parent.value);
    }
  }
}

// 在树中找到某个 value 的父节点（O(N)，Demo 够用）
function findParent(list, childValue) {
  for (const n of list) {
    if (n.children?.some((c) => c.value === childValue)) return n;
    if (n.children?.length) {
      const p = findParent(n.children, childValue);
      if (p) return p;
    }
  }
  return null;
}

// v-model 同步（多选）
watch(checkedValues, () => {
  emit("update:modelValue", checkedValues.value);
});


// 已选择路径的文字
const selectedLabels = computed(() => selectedPath.value.map((o) => o.label));

const panels = computed(() => {
  const root = innerOptions.value;
  if (!selectedPath.value.length) return [root];
  const result = [root];
  let current = root;
  for (const item of selectedPath.value) {
    const found = current.find((o) => o.value === item.value);
    if (found?.children) {
      result.push(found.children);
      current = found.children;
    } else {
      break;
    }
  }
  return result;
});

// =============== 搜索功能核心 ===============

// 收集所有路径（递归）
function collectPaths(options, parentPath = []) {
  let paths = [];
  for (const opt of options) {
    const newPath = [...parentPath, opt];
    paths.push(newPath);
    if (opt.children?.length) {
      paths = paths.concat(collectPaths(opt.children, newPath));
    }
  }
  return paths;
}

// 过滤路径
const filteredPaths = computed(() => {
  if (!keyword.value) return [];
  const allPaths = collectPaths(innerOptions.value);
  return allPaths.filter((path) =>
    path.some((o) => o.label.includes(keyword.value))
  );
});

// 输入时清理状态
function onInput() {
  if (keyword.value) {
    selectedPath.value = [];
  }
}

// 点击搜索结果
function selectPath(path) {
  selectedPath.value = path;
  keyword.value = "";
  showDropdown.value = false;
}

// ============================================

async function handleSelect(option, level) {
  selectedPath.value = selectedPath.value.slice(0, level);
  selectedPath.value[level] = option;

  const parentList =
    level === 0 ? innerOptions.value : selectedPath.value[level - 1].children;
  const idx = parentList.findIndex((o) => o.value === option.value);
  if (idx === -1) return;

  // ✅ 情况1：静态 children，直接展开
  if (option.children && option.children.length) {
    selectedPath.value[level] = option;
    return;
  }

  // ✅ 情况2：异步加载
  if (option.hasChildren && !option.children && props.loadChildren) {
    parentList.splice(idx, 1, { ...toRaw(option), loading: true });

    try {
      const children = (await props.loadChildren(toRaw(option))) || [];
      parentList.splice(idx, 1, { ...toRaw(option), children });
      selectedPath.value[level] = parentList[idx];
    } finally {
      const cur = parentList[idx];
      if (cur && cur.loading) {
        parentList.splice(idx, 1, { ...cur, loading: false });
        selectedPath.value[level] = parentList[idx];
      }
    }
    return;
  }

  // ✅ 情况3：没有 children，也没有 hasChildren
  showDropdown.value = false;
}

// 判断是否高亮
function isActive(option, level) {
  return selectedPath.value[level]?.value === option.value;
}
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

function clearSelection() {
  selectedPath.value = [];
  keyword.value = "";
  showDropdown.value = false;
  emit("update:modelValue", []);
}

// v-model 同步
watch(selectedPath, () => {
  const values = selectedPath.value.map((item) => item.value);
  emit("update:modelValue", values);
});



// ========== 路径查找（返回节点数组或标签数组） ==========
function findPathByValue(options, value, path = []) {
  for (const opt of options) {
    const nextPath = [...path, opt]
    if (opt.value === value) return nextPath
    if (opt.children?.length) {
      const res = findPathByValue(opt.children, value, nextPath)
      if (res) return res
    }
  }
  return null
}

function getPathLabelsByValue(value) {
  const path = 
  (innerOptions.value, value) // 注意：innerOptions 是 ref，模板里会自动解包；这里要取 .value
  return path ? path.map(n => n.label) : []
}

function getPathText(value) {
  return getPathLabelsByValue(value).join(' / ')
}

// ========== 仅显示叶子节点为标签（更贴近 el-cascader 多选习惯） ==========
const checkedLeafValues = computed(() => {
  return checkedValues.value.filter(v => {
    const node = findNodeByValue(innerOptions.value, v)
    // 叶子判定：没有 children 且不是 hasChildren（未懒加载的父）
    return node && !(node.children?.length) && !node.hasChildren
  })
})

// 在树中找到节点（用于删除/判定）
function findNodeByValue(options, value) {
  for (const opt of options) {
    if (opt.value === value) return opt
    if (opt.children?.length) {
      const res = findNodeByValue(opt.children, value)
      if (res) return res
    }
  }
  return null
}

// ========== 标签上的“X”删除 ==========
function uncheckByValue(value) {
  const node = findNodeByValue(innerOptions.value, value)
  if (!node) return
  removeChecked(node)     // 递归移除自身+子孙
  syncAncestors(node)     // 自底向上同步半选
  emit('update:modelValue', checkedValues.value.slice())
}
</script>

<style scoped>
.cascader-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.cascader-tag {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  padding: 2px 8px;
  border-radius: 4px;
  background: #f4f5f7;
  font-size: 12px;
  line-height: 20px;
  color: #606266;
}

.cascader-tag .tag-close {
  display: inline-block;
  margin-left: 6px;
  cursor: pointer;
  font-style: normal;
  color: #909399;
}
.cascader-tag .tag-close:hover {
  color: #606266;
}

.cascader {
  position: relative;
  width: 300px;
  font-family: sans-serif;
}
.cascader-input {
  border: 1px solid #ccc;
  padding: 6px 28px 6px 10px; /* 留出右侧空隙给按钮 */
  border-radius: 4px;
}

.cascader-input-field {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
}

.cascader-display-text {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: #333;
}

.clear-btn {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  color: #999;
  cursor: pointer;
  font-size: 14px;
  user-select: none;
}

.clear-btn:hover {
  color: #333;
}
.cascader-input input {
  border: none;
  outline: none;
  width: 100%;
}
.cascader-dropdown {
  position: absolute;
  top: 110%;
  left: 0;
  display: flex;
  border: 1px solid #ddd;
  background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  z-index: 10;
  max-height: 250px;
  overflow-y: auto;
}
.cascader-panel {
  min-width: 120px;
  border-right: 1px solid #eee;
}
.cascader-option {
  padding: 6px 12px;
  cursor: pointer;
}
.cascader-option:hover {
  background: #f5f5f5;
}
.cascader-option.active {
  background: #409eff;
  color: #fff;
}
.no-data {
  padding: 8px;
  color: #999;
  text-align: center;
}
.arrow {
  float: right;
  font-size: 12px;
}
.loading {
  float: right;
  font-size: 12px;
  color: orange;
}
</style>
